<?php

namespace app\command;

use app\common\model\BoardMessage;
use app\common\model\CommentMessage;
use app\common\model\PrivateMessage;
use app\common\model\Question;
use think\console\Command;
use think\console\Input;
use think\console\Output;
use app\common\model\User;
use app\common\model\Type;
use app\common\services\TypeConsistent;
use RuntimeException;
use think\Db;

class MockData extends Command
{
    protected function configure()
    {
        // 指令配置
        $this->setName('util:mockdata')
            ->setDescription('生成模拟数据');
    }

    protected function execute(Input $input, Output $output)
    {
        // 开启事务模式
        $db = db();
        $db->startTrans();

        try {
            $data = $this->mockAll();
            extract($data);

            // 提交
            $db->commit();
        } catch (\Exception $e) {
            $db->rollback();
            $output->error('Mock error:' . $e->getMessage());
        }
    }

    private function mockAll()
    {
        $users = $this->mockUsers([
            ['UserA', '123456', 0],
            ['UserB', '123456', 1],
            ['UserC', '123456', 2],
            ['UserD', '123456', 3],
        ]);

        $types = $this->mockTypes([
            '生活类',
            '科技类',
            '社会类',
            '历史类',
        ]);

        $questions = $this->mockQuestions([
            'types' => $types,
            'authors' => $users,
            'styles' => [0, 1],
            'images' => ['G0470.jpg', 'G0473.jpg', null],
            'texts' => [
                '哀其不幸，怒其不争。',
                '真的猛士，敢于直面惨淡的人生，敢于正视淋漓的鲜血。',
                '猛兽总是独行，牛羊才成群结队。',
                '中国人的性情是总喜欢调和折中的。',
                '从来如此，便对么？',
                '面具戴太久，就会长到脸上，再想揭下来，除非伤筋动骨扒皮。',
                '其实地上本没有路，走的人多了，也便成了路。',
                '贪安稳就没有自由，要自由就要历些危险。只有这两条路。',
            ],
            'options' => [
                'Python',
                'Java',
                'C/C++',
                'PHP',
                'JavaScript',
                'Kotlin',
                'Dart'
            ],
            'answers' => [
                '回收站',
                'IE',
                '微信',
                'QQ',
                'Chrome',
                'VSCode'
            ],
            'status' => [0, 1, 2],
            'pass_count' => [3, 50],
            'fail_count' => [3, 50],
            'count' => 20,
        ]);

        $comments = $this->mockComments([
            'questions' => $questions,
            'commenters' => $users,
            'contents' => [
                '留言1',
                '留言2',
                '留言3',
                '留言4',
                '留言5',
            ],
        ]);

        $boardMessages = $this->mockBoardMessages([
            'authors' => $users,
            'contents' => [
                '人类的历史，就是一个不断地从必然王国向自由王国发展的历史。',
                '只有人们的社会实践，才是人们对于外界认识的真理性的标准。真理的标准只能是社会的实践。',
                '中国人有一句老话：“不入虎穴，焉得虎子。”这句话对于人们的实践是真理，对于认识论也是真理。离开实践的认识是不可能的。',
                '睡眠和休息丧失了时间，却取得了明天工作的精力。',
                '马克思列宁主义并没有结束真理，而是在实践中不断地开辟认识真理的道路。',
                '好好学习，天天向上。',
                '情况是在不断地变化，要使自己的思想适应新的情况，就得学习。',
                '我们要振作精神，下苦功学习。下苦功，三个字，一个叫下，一个叫苦，一个叫功，一定要振作精神，下苦功。',
            ],
            'count' => 50,
        ]);

        $users2 = $this->mockUsers2('U_%03d', '123456', 1, 30);
        $this->mockPrivateMessages([
            'contents' => [
                '人类的历史，就是一个不断地从必然王国向自由王国发展的历史。',
                '只有人们的社会实践，才是人们对于外界认识的真理性的标准。真理的标准只能是社会的实践。',
                '中国人有一句老话：“不入虎穴，焉得虎子。”这句话对于人们的实践是真理，对于认识论也是真理。离开实践的认识是不可能的。',
                '睡眠和休息丧失了时间，却取得了明天工作的精力。',
                '马克思列宁主义并没有结束真理，而是在实践中不断地开辟认识真理的道路。',
                '好好学习，天天向上。',
                '情况是在不断地变化，要使自己的思想适应新的情况，就得学习。',
                '我们要振作精神，下苦功学习。下苦功，三个字，一个叫下，一个叫苦，一个叫功，一定要振作精神，下苦功。',
            ],
            'senders' => $users2,
            'receivers' => $users
        ]);

        return [
            'users' => $users,
            'types' => $types,
            'questions' => $questions,
            'comments' => $comments,
            'boardMessages' => $boardMessages
        ];
    }

    private function mockUsers($users)
    {
        $result = [];
        foreach ($users as $item) {
            [$username, $password, $role] = $item;
            $user = new User([
                'username' => $username,
                'realname' => $username,
                'role' => $role,
                'is_superadmin' => false,
                'is_active' => true,
            ]);
            $user->setPassword($password);
            $user->save();
            $result[] = $user;
        }
        return $result;
    }

    private function mockUsers2($format, $password, $auth, $n)
    {
        $desp = [];
        for ($i = 0; $i < $n; $i++) {
            $desp[] = [sprintf($format, $i + 1), $password, $auth];
        }
        return $this->mockUsers($desp);
    }

    private function mockTypes($types)
    {
        if (Type::where(1)->count() > 0) {
            throw new RuntimeException('类型表必须为空');
        }
        $result = [];
        $i = 0;
        foreach ($types as $name) {
            $type1 = new Type([
                'type_name' => $name,
                'parent_type_id' => 0,
                'root_type_id' => 0,
                'output_code' => $i,
            ]);
            $type1->save();
            $type2 = new Type([
                'type_name' => $name . 'A',
                'parent_type_id' => $type1->id,
                'root_type_id' => 0,
                'output_code' => 0,
            ]);
            $type2->save();
            $type3 = new Type([
                'type_name' => $name . 'B',
                'parent_type_id' => $type1->id,
                'root_type_id' => 0,
                'output_code' => 0,
            ]);
            $type3->save();
            $result[] = $type1;
            $result[] = $type2;
            $result[] = $type3;
            $i++;
        }
        $allTypes = Type::where(1)->select();
        // $allTypes = $result;
        $consistent = new TypeConsistent($allTypes);
        $consistent->fixConsistent();
        return $result;
    }

    private function buildRand($array, $min = 1, $max = null)
    {
        if ($max === null) {
            $max = $min;
        }
        return function () use ($array, $min, $max) {
            if ($min === $max && $min === 1) {
                $index = array_rand($array, 1);
                return $array[$index];
            } else {
                $n = rand($min, $max);
                if ($n === 1) {
                    $indexes = [array_rand($array, 1)];
                } else {
                    $indexes = array_rand($array, $n);
                }
                return array_values(array_map(
                    function ($index) use ($array) {
                        return $array[$index];
                    },
                    $indexes
                ));
            }
        };
    }

    private function buildRandInt($min = 1, $max = null)
    {
        if ($max === null) {
            $max = $min;
        }
        return function () use ($min, $max) {
            return rand($min, $max);
        };
    }

    private function randBool()
    {
        return boolval(rand(0, 1));
    }

    private function mockQuestions($description)
    {
        extract($description);
        $randType = $this->buildRand($types);
        $randAuthor = $this->buildRand($authors);
        $randStyle = $this->buildRand($styles);
        $randImage = $this->buildRand($images);
        $randText = $this->buildRand($texts);
        $randOptions = $this->buildRand($options, 2, 5);
        $randAnswer = $this->buildRand($answers);
        $randStatus = $this->buildRand($status);
        $randPass = $this->buildRandInt($pass_count[0], $pass_count[1]);
        $randFail = $this->buildRandInt($fail_count[0], $fail_count[1]);

        $result = [];
        for ($i = 0; $i < $count; $i++) {
            $question = new Question([
                'type_id' => $randType()->id,
                'author_id' => $randAuthor()->id,
                'style' => $randStyle(),
                'image' => $randImage(),
                'text' => $randText(),
                'status' => $randStatus(),
                'pass_count' => $randPass(),
                'fail_count' => $randFail(),
                'is_exported' => false,
            ]);
            if ($question->style === 0) {
                $question->options = $randOptions();
                $index = rand(0, count($question->options) - 1);
                $question->answer = chr(65 + $index);
            } else {
                $question->options = [];
                $question->answer = $randAnswer();
            }
            $question->save();
            $result[] = $question;
        }
        return $result;
    }

    private function mockComments($description)
    {
        extract($description);
        $randContent = $this->buildRand($contents, 1, 5);
        $randCommenter = $this->buildRand($commenters);

        $result = [];
        foreach ($questions as $question) {
            $contentList = $randContent();
            foreach ($contentList as $content) {
                $comment = new CommentMessage([
                    'question_id'     => $question->id,
                    'commenter_id'    => $randCommenter()->id,
                    'request_check'   => $rc = $this->randBool(),
                    'is_rechecked'    => $rc ? $this->randBool() : false,
                    'content'         => $content,
                ]);
                $comment->save();
                $result[] = $comment;
            }
        }
        return $result;
    }

    private function mockBoardMessages($description)
    {
        extract($description);
        $randAuthor = $this->buildRand($authors);
        $randContent = $this->buildRand($contents);

        $result = [];
        for ($i = 0; $i < $count; $i++) {
            $message = new BoardMessage();
            $message->author_id = $randAuthor()->id;
            $message->content = $randContent();
            $message->save();

            $result[] = $message;
        }
        return $result;
    }

    private function mockPrivateMessages($description)
    {
        extract($description);
        $randContent = $this->buildRand($contents);

        foreach ($senders as $k => $s) {
            foreach ($receivers as $r) {
                $n = $k === 0 ? rand(20, 50) : 1;
                for ($i = 0; $i < $n; $i++) {
                    if (boolval(rand(0, 1))) {
                        $to_id = $r->id;
                        $from_id = $s->id;
                    } else {
                        $to_id = $s->id;
                        $from_id = $r->id;
                    }
                    $message = new PrivateMessage();
                    $message->message_type = 'null';
                    $message->to_id = $to_id;
                    $message->from_id = $from_id;
                    $message->content = $randContent();
                    $message->payload = [];
                    $message->is_read = false;
                    $message->save();
                }
            }
        }
    }
}
